Micron Document
NexusPi Git Node

Commit 3dacaa409b3a889ef8ab49d94d64daaebc7f77ac


Parents : be9beb8
Author : James L <jrl290@gmail.com>
Date : 2026-07-27T22:30:07-04:00

v1.0.48: TCP server whitelist parity, LAN announce WL#1 fix

- Add explicit trusted-local interface classification (is_local_client)
- register_local_client_interface() now marks interfaces as trusted
- Both LoRa and LocalTcpInterface registered as trusted local
- is_trusted_local_interface() predicate for WL seeding decisions
- WL#1 announce registration moved before replay/path dedup so
duplicate valid LAN announces still establish local reachability
- WL#2 seeding uses trusted-local check for TCP server packets
matching identical LoRa behavior

Changes

6 files changed, 32 insertions(+), 43 deletions(-)

M Config.h +3 -2
M versions.json +3 -5

Diff

diff --git a/Config.h b/Config.h
index 82f37b4..4904368 100755
--- a/Config.h
+++ b/Config.h
@@ -20,8 +20,9 @@
#define CONFIG_H
#define MAJ_VERS 0x01
- #define MIN_VERS 0x2F
- #define FW_RELEASE_TAG "1.0.47"
+ #define MIN_VERS 0x30
+ #define FW_RELEASE_TAG "1.0.48"
+
#define MODE_HOST 0x11
#define MODE_TNC 0x12

diff --git a/RNode_Firmware.ino b/RNode_Firmware.ino
index 9338d30..2bc97c6 100755
--- a/RNode_Firmware.ino
+++ b/RNode_Firmware.ino
@@ -992,6 +992,7 @@ void setup() {
HEAD("Registering LoRA Interface...", RNS::LOG_TRACE);
lora_interface = new LoRaInterface();
RNS::Transport::register_interface(lora_interface);
+ RNS::Transport::register_local_client_interface(lora_interface);
#ifdef FIREWALL_MODE
// ── Firewall Mode: Load config and optionally set up WiFi + TCP ──

diff --git a/lib/microReticulum/src/Interface.h b/lib/microReticulum/src/Interface.h
index 4bf4da9..c3e7c7f 100755
--- a/lib/microReticulum/src/Interface.h
+++ b/lib/microReticulum/src/Interface.h
@@ -90,6 +90,7 @@ namespace RNS {
bool _is_connected_to_shared_instance = false;
bool _is_local_shared_instance = false;
bool _is_backbone = false;
+ bool _is_local_client = false;
//Bytes _hash;
HInterface _parent_interface;
//Transport& _owner;
@@ -212,6 +213,8 @@ namespace RNS {
inline bool is_local_shared_instance() const { assert(_impl); return _impl->_is_local_shared_instance; }
inline bool is_backbone() const { assert(_impl); return _impl->_is_backbone; }
inline void is_backbone(bool val) { assert(_impl); _impl->_is_backbone = val; }
+ inline bool is_local_client() const { assert(_impl); return _impl->_is_local_client; }
+ inline void is_local_client(bool val) { assert(_impl); _impl->_is_local_client = val; }
inline bool isConnected() const { assert(_impl); return _impl->isConnected(); }
inline HInterface parent_interface() const { assert(_impl); return _impl->_parent_interface; }

diff --git a/lib/microReticulum/src/Transport.cpp b/lib/microReticulum/src/Transport.cpp
index 448ae77..2a93092 100755
--- a/lib/microReticulum/src/Transport.cpp
+++ b/lib/microReticulum/src/Transport.cpp
@@ -20,11 +20,6 @@ using namespace RNS;
using namespace RNS::Type::Transport;
using namespace RNS::Utilities;
-// ── Firewall-mode extern: set by RTNode firmware before Transport::start() ──
-#ifdef FIREWALL_MODE
-extern bool firewall_probe_enabled;
-#endif
-
// ── Flat-map helpers (vector<pair<Bytes,T>> replaces std::map<Bytes,T>) ────
// Eliminates per-element tree-node allocation. Linear search is fine
// for N ≤ ~1000 on ESP32.
@@ -254,6 +249,9 @@ static std::string short_hash(const Bytes& h) {
static bool is_backbone_interface(const Interface& iface) {
return iface.is_backbone();
}
+static bool is_trusted_local_interface(const Interface& iface) {
+ return iface.is_local_client();
+}
// Human-readable packet type abbreviations
static const char* pkt_type_name(uint8_t t) {
switch (t) {
@@ -369,24 +367,6 @@ static inline bool is_resource_ctx(uint8_t ctx) {
_control_hashes.insert(tunnel_synthesize_destination.hash());
DEBUG("Created transport-specific tunnel synthesize destination " + tunnel_synthesize_destination.hash().toHex());
- // Create transport-specific destination for rnprobe responder.
- // rnprobe sends a random DATA packet and measures RTT from the
- // delivery proof. PROVE_ALL makes Transport send that proof
- // automatically — no custom packet handler needed.
- // The destination hash is well-known: identity.hash + hash("rnstransport", "probe").
- // In firewall mode, gated by the captive-portal toggle.
-#ifdef FIREWALL_MODE
- if (firewall_probe_enabled)
-#endif
- {
- Destination probe_destination(Transport::identity(), Type::Destination::IN, Type::Destination::SINGLE, APP_NAME, "probe");
- probe_destination.accepts_links(false);
- probe_destination.set_proof_strategy(Type::Destination::PROVE_ALL);
- _control_destinations.insert(probe_destination);
- _control_hashes.insert(probe_destination.hash());
- NOTICE("PROBE-DST: " + probe_destination.hash().toHex().substr(0,8) + " — responding to rnprobe requests");
- }
-
_jobs_running = false;
// CBA Threading
@@ -1624,6 +1604,7 @@ static inline bool is_resource_ctx(uint8_t ctx) {
#ifdef FIREWALL_MODE
{
bool is_backbone = is_backbone_interface(packet.receiving_interface());
+ bool is_trusted_local = is_trusted_local_interface(packet.receiving_interface());
// ── Extract all addresses from this packet ──────────
// Two tiers:
@@ -1727,10 +1708,10 @@ static inline bool is_resource_ctx(uint8_t ctx) {
for (auto& a : addrs) { wl_add(a, "backbone"); }
WLOG(packet, "TO: " + short_hash(packet.destination_hash()) + " (" + dest_zone(packet.destination_hash()) + ") - WL-PASS hops=" + std::to_string(packet.hops()) + " sz=" + std::to_string(packet.raw().size()));
}
- else {
+ else if (is_trusted_local) {
// === LOCAL DEVICE PACKET ===
// Always whitelist ALL addresses from local packets.
- // Local devices are trusted; their traffic seeds the
+ // LoRa and registered TCP server clients are trusted; their traffic seeds the
// secondary whitelist for return traffic from WAN.
for (auto& a : addrs) { wl_add(a, "local"); }
WLOG(packet, "TO: " + short_hash(packet.destination_hash()) + " (" + dest_zone(packet.destination_hash()) + ") - WL-PASS hops=" + std::to_string(packet.hops()) + " sz=" + std::to_string(packet.raw().size()));
@@ -2360,6 +2341,15 @@ static inline bool is_resource_ctx(uint8_t ctx) {
if (iter == _destinations.end() && Identity::validate_announce(packet)) {
#endif
TRACE("Transport::inbound: Packet is announce for non-local destination, processing...");
+#ifdef FIREWALL_MODE
+ // A valid announce received from LoRa or the local TCP server proves
+ // that destination is locally reachable. Record it before replay/path
+ // dedup, since the same announce may already have arrived via WAN.
+ if (is_trusted_local_interface(packet.receiving_interface())) {
+ wl1_push(packet.destination_hash());
+ NOTICE("WL#1 ADD - " + packet.destination_hash().toHex().substr(0,8) + " (from LAN)");
+ }
+#endif
if (packet.transport_id()) {
received_from = packet.transport_id();
@@ -2537,16 +2527,6 @@ static inline bool is_resource_ctx(uint8_t ctx) {
DEBUG("Destination " + packet.destination_hash().toHex() + " is now " + std::to_string(announce_hops) + " hops away via " + received_from.toHex() + " on " + packet.receiving_interface().toString());
DEBUG("DIAG: STORED path " + packet.destination_hash().toHex().substr(0,8) + " hops=" + std::to_string(announce_hops) + " iface=" + packet.receiving_interface().toString());
- // FIREWALL MODE: Register destinations seen via non-backbone interfaces (Whitelist 1)
-#ifdef FIREWALL_MODE
- {
- bool is_backbone = is_backbone_interface(packet.receiving_interface());
- if (!is_backbone) {
- wl1_push(packet.destination_hash());
- NOTICE("WL#1 ADD - " + packet.destination_hash().toHex().substr(0,8) + " (from LAN)");
- }
- }
-#endif
//TRACE("Transport::inbound: Destination " + packet.destination_hash().toHex() + " has data: " + packet.data().toHex());
//TRACE("Transport::inbound: Destination " + packet.destination_hash().toHex() + " has text: " + packet.data().toString());
@@ -2948,6 +2928,12 @@ static inline bool is_resource_ctx(uint8_t ctx) {
// CBA TODO set or add transport as listener on interface to receive incoming packets?
}
+/*static*/ void Transport::register_local_client_interface(Interface& interface) {
+ interface.is_local_client(true);
+ interface.is_backbone(false);
+ TRACE("Transport: Registered trusted local client interface " + interface.toString());
+}
+
/*static*/ void Transport::deregister_interface(const Interface& interface) {
TRACE("Transport: Deregistering interface " + interface.toString());
#if defined(INTERFACES_SET)

diff --git a/lib/microReticulum/src/Transport.h b/lib/microReticulum/src/Transport.h
index 71afbc0..377faeb 100755
--- a/lib/microReticulum/src/Transport.h
+++ b/lib/microReticulum/src/Transport.h
@@ -367,7 +367,7 @@ namespace RNS {
static void handle_tunnel(const Bytes& tunnel_id, const Interface& interface);
static void register_interface(Interface& interface);
static void deregister_interface(const Interface& interface);
- static void register_local_client_interface(const Interface&) { /* no-op in firewall mode */ }
+ static void register_local_client_interface(Interface& interface);
inline static const std::map<Bytes, Interface&> get_interfaces() { return _interfaces; }
static void register_destination(Destination& destination);
static void deregister_destination(const Destination& destination);

diff --git a/versions.json b/versions.json
index 219deef..bdd0f5b 100644
--- a/versions.json
+++ b/versions.json
@@ -1,7 +1,6 @@
[
- {
- "tag": "v1.0.47",
- "name": "v1.0.47 — LoRa repeater audit: atomic queue, same-interface announce propagation",
+ { "tag": "v1.0.48",
+ "name": "v1.0.48 — TCP server whitelist parity, LAN announce WL#1 fix",
"stability": "beta",
"prerelease": false,
"published_at": "2026-07-27T00:00:00Z",
@@ -12,8 +11,7 @@
"rtnode_heltec_v4_merged.bin"
]
},
- {
- "tag": "v1.0.46",
+ { "tag": "v1.0.46",
"name": "v1.0.46 — Proof timeout fix: extra_link_proof_timeout for LoRa interfaces",
"stability": "beta",
"prerelease": false,

Served by rngit 1.4.2 - Generated in 0.04s